package org.apache.lucene.ocean;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.lucene.ocean.util.CElement;
import org.apache.lucene.ocean.util.SortedListMap;
import org.apache.lucene.ocean.util.XMLUtil;
import org.jdom.Element;

public class SnapshotInfo implements CElement, Comparable<SnapshotInfo> {
  private BigDecimal id;
  private SortedListMap<IndexID,IndexInfo> indexInfos;
  private int numDocs;
  private int maxDoc;
  private int deletedDocs;
  private long timestamp;

  public SnapshotInfo(BigDecimal id, int maxDoc, int numDocs, int deletedDocs, long timestamp) {
    assert id != null;
    this.id = id;
    this.maxDoc = maxDoc;
    this.numDocs = numDocs;
    this.deletedDocs = deletedDocs;
    this.timestamp = timestamp;
    indexInfos = new SortedListMap<IndexID,IndexInfo>();
  }
  
  public IndexInfo getIndexInfo(IndexID id) {
    return indexInfos.get(id);
  }
  
  public int compareTo(SnapshotInfo other) {
    return id.compareTo(other.id);
  }
  
  public void add(IndexInfo indexInfo) {
    indexInfos.put(indexInfo.getIndexID(), indexInfo);
  }

  public SnapshotInfo(Element element) {
    indexInfos = new SortedListMap<IndexID,IndexInfo>();
    id = new BigDecimal(element.getAttributeValue("id"));
    for (Element indexElement : XMLUtil.getChildren("index", element)) {
      IndexInfo indexInfo = new IndexInfo(indexElement);
      indexInfos.put(indexInfo.getIndexID(), indexInfo);
    }
  }

  public Long getSnapshotId() {
    return id.longValue();
  }

  public BigDecimal getId() {
    return id;
  }

  public Collection<IndexInfo> getIndexInfos() {
    return indexInfos.values();
  }

  public SnapshotInfo(BigDecimal id, Map<IndexID,IndexInfo> indexInfos) {
    this.id = id;
    this.indexInfos = new SortedListMap<IndexID,IndexInfo>(indexInfos);
  }

  public static class IndexInfo implements CElement {
    public Long snapshotId;
    public Long id;
    public Long segmentGeneration;
    public String type;
    public Integer maxDoc;
    public Integer deletedDoc;
    public Integer numDocs;
    public Long minDocumentId;
    public Long maxDocumentId;
    public Long minSnapshotId;
    public Long maxSnapshotId;
    public Long checkpointId;
    public Long lastAppliedId; // last transaction that made changes to this snapshot/index
    public List<IndexID> mergedFrom;
    
    public IndexInfo() {
    }
    
    public IndexInfo(Long snapshotId, Long id, Long segmentGeneration, String type, int maxDoc, int numDocs, int deletedDoc, Long minDocumentId, Long maxDocumentId, Long minSnapshotId, Long maxSnapshotId, Long checkpointId, Long lastAppliedId, List<IndexID> mergedFrom) {
      this.snapshotId = snapshotId;
      this.id = id;
      this.segmentGeneration = segmentGeneration;
      this.type = type;
      this.maxDoc = maxDoc;
      this.numDocs = numDocs;
      this.deletedDoc = deletedDoc;
      this.minDocumentId = minDocumentId;
      this.maxDocumentId = maxDocumentId;
      this.minSnapshotId = minSnapshotId;
      this.maxSnapshotId = maxSnapshotId;
      this.checkpointId = checkpointId;
      this.lastAppliedId = lastAppliedId;
      this.mergedFrom = mergedFrom;
    }
    
    public List<IndexID> getMergedFrom() {
      return mergedFrom;
    }
    
    public Long getLastAppliedId() {
      return lastAppliedId;
    }
    
    public IndexID getIndexID() {
      return new IndexID(id, type);
    }
    
    public IndexInfo(Element element) {
      snapshotId = XMLUtil.getAttributeLong("snapshotid", element);
      id = XMLUtil.getAttributeLong("id", element);
      segmentGeneration = XMLUtil.getAttributeLong("segmentGeneration", element);
      type = XMLUtil.getAttributeString("type", element);
      maxDoc = XMLUtil.getAttributeInteger("maxDoc", element);
      numDocs = XMLUtil.getAttributeInteger("numDocs", element);
      deletedDoc = XMLUtil.getAttributeInteger("deletedDoc", element);
      minDocumentId = XMLUtil.getAttributeLong("minDocumentId", element);
      minSnapshotId = XMLUtil.getAttributeLong("minSnapshotId", element);
      maxDocumentId = XMLUtil.getAttributeLong("maxDocumentId", element);
      maxSnapshotId = XMLUtil.getAttributeLong("maxSnapshotId", element);
      checkpointId = XMLUtil.getAttributeLong("checkpointId", element);
      lastAppliedId = XMLUtil.getAttributeLong("lastAppliedId", element);
      String mergedFromString = XMLUtil.getAttributeString("mergedFrom", element);
      if (StringUtils.isNotBlank(mergedFromString)) {
        String[] strings = StringUtils.split(mergedFromString, ",");
        mergedFrom = new ArrayList<IndexID>(strings.length);
        for (String string : strings) {
          String[] array = StringUtils.split(string, ":");
          String type = array[0];
          Long id = new Long(array[1]);
          mergedFrom.add(new IndexID(id, type));
        }
      }
    }
    
    public Integer getNumDocs() {
      return numDocs;
    }
    
    public Long getCheckpointId() {
      return checkpointId;
    }
    
    public Long getSnapshotId() {
      return snapshotId;
    }
    
    public Long getMinSnapshotId() {
      return minSnapshotId;
    }
    
    public Long getMinDocumentId() {
      return minDocumentId;
    }
    
    public Long getMaxSnapshotId() {
      return maxSnapshotId;
    }
    
    public Long getMaxDocumentId() {
      return maxDocumentId;
    }
    
    public Integer getDeletedDoc() {
      return deletedDoc;
    }
    
    public Long getSegmentGeneration() {
      return segmentGeneration;
    }
    
    public Integer getMaxDoc() {
      return maxDoc;
    }
    
    public Long getId() {
      return id;
    }

    public String getType() {
      return type;
    }

    public Element toElement() {
      Element element = new Element("index");
      XMLUtil.setAttribute("snapshotid", snapshotId, element);
      XMLUtil.setAttribute("type", type, element);
      XMLUtil.setAttribute("id", id, element);
      XMLUtil.setAttribute("segmentGeneration", segmentGeneration, element);
      XMLUtil.setAttribute("maxDoc", maxDoc, element);
      XMLUtil.setAttribute("numDocs", numDocs, element);
      XMLUtil.setAttribute("deletedDoc", deletedDoc, element);
      XMLUtil.setAttribute("minDocumentId", minDocumentId, element);
      XMLUtil.setAttribute("maxDocumentId", maxDocumentId, element);
      XMLUtil.setAttribute("minSnapshotId", minSnapshotId, element);
      XMLUtil.setAttribute("maxSnapshotId", maxSnapshotId, element);
      XMLUtil.setAttribute("checkpointId", checkpointId, element);
      XMLUtil.setAttribute("lastAppliedId", lastAppliedId, element);
      if (mergedFrom != null && mergedFrom.size() > 0) {
        String mergedFromString = StringUtils.join(mergedFrom, ",");
        XMLUtil.setAttribute("mergedFrom", mergedFromString, element);
      }
      return element;
    }
  }
  /**
  public void writeTo(RandomAccessIO output) throws Exception {
    Element element = toElement();
    String xml = XMLUtil.outputElement(element);
    byte[] bytes = xml.getBytes("UTF-8");
    output.write(bytes);
  }
  **/
  public Element toElement() {
    Element element = new Element("snapshot");
    XMLUtil.setAttribute("id", id, element);
    XMLUtil.setAttribute("numDocs", numDocs, element);
    XMLUtil.setAttribute("maxDoc", maxDoc, element);
    XMLUtil.setAttribute("deletedDocs", deletedDocs, element);
    DateFormat dateFormat = DateFormat.getDateTimeInstance();
    String timeString = dateFormat.format(new Date(timestamp));
    XMLUtil.setAttribute("timestamp", timeString, element);
    for (IndexInfo indexInfo : indexInfos.values()) {
      element.addContent(indexInfo.toElement());
    }
    return element;
  }
}